package com.hissy.cn.redis;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;

import com.alibaba.fastjson.JSON;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

/**
 * Created by zhangyanfei on 2019/1/28.
 * 获取不同namespace的redis配置
 * @author zhangyanfei
 * @date 2019/01/28
 */
public class JedisConfigFactory {

    private static final Logger logger= LoggerFactory.getLogger(JedisConfigFactory.class);
    /**
     *  本地缓存 key:namespace value:configMap
     */
    private static Map<String, Map<String, Object>> CONFIG_SPACE_MAPPING = new HashMap<>();
    /**
     *  最终生效的jedis配置
     */
    private static Map<String, Map<String, Object>> EFFECTIVE_CONFIG_SPACE_MAPPING = new HashMap<>();
    /**
     * 加载是否完成
     */
    private static boolean IS_INIT_DONE=false;
    /**
     * 可重入锁
     */
    private static final ReentrantLock lock=new ReentrantLock();

    /**
     * 初始化配置工厂
     */
    private static void initJedisConfigFactory() {

        Map<String,Object> sourceMap=new HashMap<>();

        try {
            InputStream inputStream=new BufferedInputStream(new FileInputStream(new File("/Users/zhangyanfei/workspace/springboot-template/common/src/main/resources/jedis.properties")));

            Properties propertiesMap=new Properties();
            propertiesMap.load(inputStream);
            for(Map.Entry entry:propertiesMap.entrySet()){
                sourceMap.put(entry.getKey().toString(),entry.getValue());
            }
        }catch (Exception e){
            logger.error("load file failed!",e);
        }


        // 加载配置文件 并转换成map
        Map<String,Object> properties=sourceMap;
        if(CollectionUtils.isEmpty(properties)){
            logger.error("init jedisConfigFactory error, properties is null");
            throw new IllegalArgumentException("jedisConfigFactory properties is null");
        }
        //按照namespace 分组
        JedisConfigFactory.resolveConfigProperties(properties);
        //对配置正确性进行校验
        JedisConfigFactory.validPropertiesKeys();

        //验证是否已经初始化完成
        if(!CollectionUtils.isEmpty(EFFECTIVE_CONFIG_SPACE_MAPPING)){
            //加载的 Effective jedisConfig 有值
            IS_INIT_DONE=true;
        }else{
           logger.error("init jedisConfigFactory error, properties info:{}",JSON.toJSONString(properties));
            throw new IllegalArgumentException("无法解析jedisconfig配置信息，请确认是否已正确配置或加载！");
        }
    }

    /**
     * 将配置按namespace    分组
     * @param properties  原配置文件信息
     */
    private static void resolveConfigProperties(Map<String,Object> properties){
        for(Map.Entry<String,Object> entry:properties.entrySet()){
            String key=entry.getKey();
            Object value=entry.getValue();

            String namespace=getNameSpace(key);
            if(!CONFIG_SPACE_MAPPING.containsKey(namespace)){
                // 没有就创建一个分组
                CONFIG_SPACE_MAPPING.put(namespace, new HashMap<>());
            }

            CONFIG_SPACE_MAPPING.get(namespace).put(key,value);
        }
    }

    /**
     * 校验分组的配置是否全
     * 可用的才放到Effectiveap里
     */
    private static void validPropertiesKeys() {
        for (Map.Entry<String, Map<String, Object>> entry : CONFIG_SPACE_MAPPING.entrySet()) {
            boolean ifPass = true;
            String nameSpaceKey = entry.getKey();
            Map<String, Object> outValueMap = entry.getValue();

            if (CollectionUtils.isEmpty(outValueMap)) {
                continue;
            }
            // 配置key少于要求配置
            if (outValueMap.size() < JedisPoolBeanConstant.FIELD_CONSTANT_SET.size()) {
                logger.error("配置校验不通过：缺少部分配置key. namespace:{}. config:{}", nameSpaceKey, JSON.toJSONString(outValueMap));
                continue;
            }

            if (ifPass) {
                EFFECTIVE_CONFIG_SPACE_MAPPING.put(nameSpaceKey, outValueMap);
            }
        }
    }

    /**
     * 取namespace 的规则
     * redis.namespace.port
     * @param key
     * @return
     */
    private static String getNameSpace(String key) {
        return key.substring(key.indexOf(".") + 1, key.lastIndexOf("."));
    }

    /**
     * 单例
     * 锁
     * double check
     * @return
     */
    static Map<String,JedisPoolBean> getJedisConfigList(){
        // init
        if(!IS_INIT_DONE){
            lock.lock();
            try{
                if(!IS_INIT_DONE){
                    JedisConfigFactory.initJedisConfigFactory();
                }
            }finally {
                lock.unlock();
            }
        }

        // 解析jedisPoolBean
        Map<String,JedisPoolBean> poolBeanMap=new HashMap<>(CONFIG_SPACE_MAPPING.size());
        for(Map.Entry<String,Map<String,Object>> entry: CONFIG_SPACE_MAPPING.entrySet()){
            String key=entry.getKey();
            Map<String,Object> value=entry.getValue();
            // 创建jedisPoolBean
            JedisPoolBean jedisPoolBean=JedisConfigFactory.createJedisPoolBean(key,value);

            if(jedisPoolBean!=null){
                poolBeanMap.put(key,jedisPoolBean);
            }
        }
        return poolBeanMap;
    }

    /**
     * 创建JedisPoolBean
     * @param key
     * @param value
     * @return
     */
    private static JedisPoolBean createJedisPoolBean(String key, Map<String, Object> value) {
        // 匹配配置文件属性和bean属性
        String redisHost = null;
        int redisPort = 0;
        String redisPassword = null;
        int redisDatabase = 0;
        int maxTotal = 0;
        int maxIdle = 0;
        int maxWaitMillis = 0;
        try {
            for (Map.Entry<String, Object> entryInner : value.entrySet()) {
                String innerPropertiesKey = entryInner.getKey();
                String shortKey = JedisConfigFactory.getShortKey(innerPropertiesKey);
                String innerValue = entryInner.getValue().toString().trim();
                switch (shortKey) {
                    case JedisPoolBeanConstant.REDISHOST:
                        redisHost = innerValue;
                        break;
                    case JedisPoolBeanConstant.REDISPORT:
                        redisPort = Integer.parseInt(innerValue);
                        break;
                    case JedisPoolBeanConstant.REDISPASSWORD:
                        redisPassword = innerValue;
                        break;
                    case JedisPoolBeanConstant.REDISDATABASE:
                        redisDatabase = Integer.parseInt(innerValue);
                        break;
                    case JedisPoolBeanConstant.MAXTOTAL:
                        maxTotal = Integer.parseInt(innerValue);
                        break;
                    case JedisPoolBeanConstant.MAXIDLE:
                        maxIdle = Integer.parseInt(innerValue);
                        break;
                    case JedisPoolBeanConstant.MAXWAITMILLIS:
                        maxWaitMillis = Integer.parseInt(innerValue);
                        break;
                    default:
                        break;
                }
            }
        } catch (Exception e) {
            logger.error("JedisPoolBean, The namespace for 【" + key + "】 initialization failed", e);
            return null;
        }

        // 再进行一次校验
        boolean validResult = JedisConfigFactory.repeatValidPKeys(redisHost, redisPort, redisPassword);
        if (validResult) {
            return JedisPoolBean.builder()
                .redisHost(redisHost)
                .redisPassword(redisPassword)
                .redisPort(redisPort)
                .redisDatabase(redisDatabase)
                .maxTotal(maxTotal)
                .maxIdle(maxIdle)
                .maxWaitMillis(maxWaitMillis)
                .namespace(key)
                .build();
        }
        return null;
    }

    private static String getShortKey(String key) {
        return key.substring(key.lastIndexOf(".") + 1, key.length());
    }

    /**
     * 结合value一起的校验
     *
     * @return boolean
     * @author sxx
     * @date 2018-08-10 18:08
     * @note sxx@2018-08-10 18:08创建
     */
    private static boolean repeatValidPKeys(String redisHost,
                                            int redisPort,
                                            String redisPassword) {
        boolean ifPass = true;
        if (StringUtils.isBlank(redisHost) || redisPort == 0) {
            ifPass = false;
        }
        return ifPass;
    }
}
